package cn.taketoday.live.handler;

import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import cn.taketoday.live.config.GlobalData;
import cn.taketoday.live.config.settings.AutoReplySetting;
import cn.taketoday.live.data.AutoReply;
import cn.taketoday.live.http.BilibiliHttpRequests;
import cn.taketoday.live.http.HttpRoomData;
import cn.taketoday.live.tools.CurrencyTools;
import cn.taketoday.live.utils.JodaTimeUtils;

/**
 * @author TODAY 2021/5/21 21:01
 */
public class BarrageHandler {
  static final ScheduledThreadPoolExecutor executor =
//          new ThreadPoolExecutor(5, 20, 20, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100),
//                                          new ThreadPoolExecutor.CallerRunsPolicy());
          new ScheduledThreadPoolExecutor(5, new ThreadPoolExecutor.CallerRunsPolicy());

  private static boolean close = false;
  private static boolean autoReply = true;
  private static Set<AutoReplySetting> autoReplyConfig;

  static {
    executor.setMaximumPoolSize(20);
    executor.setKeepAliveTime(20, TimeUnit.SECONDS);
    Runtime.getRuntime().addShutdownHook(new Thread(executor::shutdown));

    new Thread(new SendingRunnable()).start();
  }

  public static ThreadPoolExecutor getExecutor() {
    return executor;
  }

  public static void setClose(boolean close) {
    BarrageHandler.close = close;
  }

  public static boolean isClose() {
    return close;
  }

  static final Set<String> messages = Collections.newSetFromMap(new ConcurrentHashMap<>());

  /**
   * 发送弹幕
   *
   * @param message
   *         弹幕信息
   */
  public static void send(String message) {
    if (close || StringUtils.isEmpty(message)) {
      return;
    }
    messages.add(message);
  }

  static class SendingRunnable implements Runnable {
    volatile boolean running = true;

    @Override
    public void run() {
      while (running) {
        if (!messages.isEmpty()) {
          final Iterator<String> iterator = messages.iterator();
          final String message = iterator.next();

          int strLength = message.length();
          int maxLength = GlobalData.USER_BARRAGE_MESSAGE.getDanmu().getLength();
          if (strLength > maxLength) {
            int num = (int) Math.ceil((float) maxLength / (float) strLength);
            for (int i = 0; i <= num; i++) {
              final String substring = StringUtils.substring(
                      message, i * maxLength,
                      Math.min(strLength, maxLength * (i + 1)));

              send(substring);
            }
          }
          else {
            BilibiliHttpRequests.sendBarrage(message);
          }

          messages.remove(message);
        }

        try {
          Thread.sleep(1455);
        }
        catch (InterruptedException e) {
        }
      }
    }
  }

  public static Set<AutoReplySetting> getAutoReplyConfig() {
    return autoReplyConfig;
  }

  public static void setAutoReplyConfig(Set<AutoReplySetting> autoReplyConfig) {
    BarrageHandler.autoReplyConfig = autoReplyConfig;
  }

  public static void setAutoReply(boolean autoReply) {
    BarrageHandler.autoReply = autoReply;
  }

  public static boolean isAutoReply() {
    return autoReply;
  }

  public static void autoReply(AutoReply autoReply) {
    if (!isAutoReply()) {
      return;
    }
    for (AutoReplySetting autoReplySetting : getAutoReplyConfig()) {
      final Set<String> shields = autoReplySetting.getShields();

      if (!CollectionUtils.isEmpty(shields)) {
        for (final String shield : shields) {
          if (autoReply.getBarrage().contains(shield)) {
            return;
          }
        }
      }

      final String barrage = autoReply.getBarrage();
      final LinkedHashSet<String> keywordsToUse = new LinkedHashSet<>();
      for (String keyword : autoReplySetting.getKeywords()) {
        if (StringUtils.indexOf(keyword, "||") != -1) {
          Collections.addAll(keywordsToUse, StringUtils.split(keyword, "||"));
        }
        else {
          keywordsToUse.add(keyword);
        }
      }

      for (final String keyword : keywordsToUse) {
        if (Objects.equals(keyword, barrage)
                || (!autoReplySetting.isAccurate() && autoReply.getBarrage().contains(keyword))) {
          System.err.println(autoReply);
          autoReply(autoReplySetting, autoReply);
          return;
        }
      }
    }
  }

  public static void autoReply(AutoReplySetting replySetting, AutoReply autoReply) {

    Map<String, String> variables = new HashMap<>();

    variables.put("NAME", autoReply.getName());
    variables.put("FANS", String.valueOf(GlobalData.FAN_SNUM));
    variables.put("TIME", JodaTimeUtils.format(new Date(), TimeZone.getTimeZone("GMT+08:00"), "yyyy-MM-dd HH:mm:ss"));

    variables.put("LIVETIME", CurrencyTools.getGapTime(
            System.currentTimeMillis() - HttpRoomData.httpGetRoomInit(GlobalData.ROOM_ID).getLive_time() * 1000)
    );
    variables.put("HOT", GlobalData.ROOM_POPULARITY.toString());

    String template = replySetting.getReply();

    for (final Map.Entry<String, String> entry : variables.entrySet()) {
      final String key = entry.getKey();
      final String value = entry.getValue();
      template = StringUtils.replace(template, '%' + key + '%', value);
    }

    if (!StringUtils.isEmpty(template)) {
      send(template);
    }
  }

}
